<?php

vendor('trans_object');

class Operation extends AppModel
{
	var $name = 'Operation';
	var $useTable = false;

	/**
	 * Выполняет пополнение счёта клиента. Используется транзакция.
	 *
	 * @param &DomainBill $bill
	 * @param &array $client Стандартной формы хеш моделей. Обязательное поле [Client][<primaryKey>].
	 */
	function refill_account(DomainBill &$bill, &$client, $params=array())
	{
	    $Client =& $this->getModel('Client'); /* @var $Client Client */
	    $Pass   =& $this->getModel('Pass');   /* @var $Pass   Pass */

	    $tran = new TransObject($Pass);

	    if (!isset($client[$Client->name][$Client->primaryKey])) {
	        throw new Exception("Invalid client record", 1026);
	    }
	    $client_id = intval($client[$Client->name][$Client->primaryKey]);

	    $Client->rel();
	    $client_data = $Client->read($Client->name.'.balance', $client_id);
	    if (empty($client_data)) {
	        throw new Exception("Invalid client record", 1027);
	    }
	    $balance_before = DomainAmount($client_data[$Client->name]['balance']); /* @var $balance_before DomainAmount */

		$Pass->exec('REFILL_ACCOUNT', $client_id, $bill->amount, $balance_before, array('bill' => $bill->name, 'comment' => $client['Client']['comment']));
	    $tran->commit();
	}

	/**
	 * Транзакция платы за проведённый тест.
	 *
	 * @param integer $candidate_id Ид дкандидата
	 * @param integer $testing_id Идентификатор пройденного тестирования
	 * @param integer $params Дополнительные параметры
	 */
	function paymentTest($candidate_id, $testing_id, $answers, $params=array())
	{
	    $params['from_blank'] = false;
	    $params['enter_result'] = false;
        return $this->_paymentTest($candidate_id, $testing_id, $answers, $params);
	}

	/**
	 * Транзакция платы за ввод ответов с бланка.
	 *
	 * @param integer $candidate_id Ид дкандидата
	 * @param integer $testing_id Идентификатор пройденного тестирования
	 * @param integer $params Дополнительные параметры
	 */
	function fillInTest($candidate_id, $testing_id, $answers, $params=array())
	{
	    $params['from_blank'] = true;
	    $params['enter_result'] = false;
        return $this->_paymentTest($candidate_id, $testing_id, $answers, $params);
	}

	/**
	 * Транзакция платы за ввод ответов явно.
	 *
	 * @param integer $candidate_id Ид дкандидата
	 * @param integer $testing_id Идентификатор пройденного тестирования
	 * @param integer $params Дополнительные параметры
	 */
	function enterResultTest($candidate_id, $testing_id, $answers, $params=array())
	{
	    $params['from_blank'] = false;
	    $params['enter_result'] = true;
        return $this->_paymentTest($candidate_id, $testing_id, $answers, $params);
	}

	protected function _paymentTest($candidate_id, $testing_id, $answers, $params=array())
	{
	    $Pass        =& $this->getModel('Pass');        /* @var $Pass        Pass */
	    $Testing     =& $this->getModel('Testing');     /* @var $Testing     Testing */
	    $ClientAdmin =& $this->getModel('ClientAdmin'); /* @var $ClientAdmin ClientAdmin */

	    $tran = new TransObject($Pass);

	    $from_blank = !empty($params['from_blank']);
	    $enter_result = !empty($params['enter_result']);

 		$Testing->rel('Candidate | Client | Test');
	    $testing_conds = array();
	    $testing_conds[]["`Testing`.`candidate_id`"] = $candidate_id;
	    $testing_conds[]["`Testing`.`id`"] = $testing_id;
        $testing_conds[]["`Testing`.`pass_status`"] = '!=PASSED';
        if (($from_blank || $enter_result) && Auth::o()->is('client_admin')) {
            $auth_data = Auth::o()->data();
            $testing_conds[]["`Testing`.`client_id`"] = $auth_data['client_id'];
            if (! Auth::o()->is('ARCHIVE')) {
                $testing_conds[]["`Candidate`.`archived`"] = "N";
            }
        }
        if (! $from_blank) {
            $testing_conds[]["`Testing`.`pass_status`"] = 'IN_PROGRESS';
        }
	    $tmp = $Testing->find($testing_conds);

	    $testing   = @$tmp['Testing'];
	    $test      = @$tmp['Test'];
	    $candidate = @$tmp['Candidate'];
	    $client    = @$tmp['Client'];
	    unset($tmp);

	    if (empty($testing) || empty($test) || empty($candidate) || empty($client)) {
	        throw new ArgException("Unable to restore some data from the database", 1001);
	    }

        $testing_data = array();
        $testing_data['Testing']['id'] = $testing_id;
        $testing_data['Testing']['result'] = $answers;
        if ($from_blank) {
            $testing_data['Testing']['result_source'] = 'BLANK';
        } elseif ($enter_result) {
            $testing_data['Testing']['result_source'] = 'DIRECT';
        } else {
            $testing_data['Testing']['result_source'] = 'APPLET';
        }
        $testing_data['Testing']['pass_status'] = 'PASSED';
    	if (! $Testing->save($testing_data)) {
	        throw new Exception("Unable to change testing pass_status", 1003);
    	}
    	if (! $from_blank) {
    		$Pass->exec('PAYMENT_TEST', $client['id'], DomainAmount($test['price_test']), DomainAmount($client["balance"]), array(
                'testing_id' => $testing_id,
                'candidate_id' => $candidate['id'],
            ));
    	}

		/**
		 * Отправка писем.
		 */
        $client_admins = $ClientAdmin->getRecepientsTestEmails($client['id']);
        if (count($client_admins)) {
            $mail_params = array();
            $mail_params['candidate'] = $candidate;
            $mail_params['client'] = $client;
            $mail_params['test'] = $test;
            $mail_params['testing'] = $testing;
            $mail_params['delayed'] = true;
            if ($from_blank || $enter_result) {
                if (Auth::o()->is('admin')) {
                    $mail_params['author_admin_id'] = Auth::o()->realId();
                } elseif (Auth::o()->is('client_admin')) {
                    $mail_params['author_client_admin_id'] = Auth::o()->realId();
                }
            }
            foreach ($client_admins as $client_admin) {
                $mail_params['client_admin'] = $client_admin['ClientAdmin'];
                try {
                    $this->getModel('SendMail')->sendNoticeOnCandidateTested($mail_params);
                } catch (Exception $e) { }
            }
        }
	    $tran->commit();
	}

	/**
	 * Создание отчёта.
	 *
	 * @param integer $candidate_id Кандидат, прошедший инструмент
	 * @param integer $testing_id Запись о прошедшем инструменте
	 * @param integer $report_id Выбраный отчёт
	 * @param integer $norm_id Выбраная норма-группа
	 * @param integer $params Дополнительные параметры
	 */
	function generateReport($candidate_id, $testing_id, $report_id, $norm_id, $params=array())
	{
	    $Testing   =& $this->getModel('Testing');   /* @var $Testing   Testing */
	    $Report    =& $this->getModel('Report');    /* @var $Report    Report */
	    $Pass      =& $this->getModel('Pass');      /* @var $Pass      Pass */

	    $tran = new TransObject($Pass);

	    $Testing->rel('Candidate | Client | Test');
	    $testing_conds = array();
	    $testing_conds[]["`Testing`.`candidate_id`"] = $candidate_id;
	    $testing_conds[]["`Testing`.`id`"] = $testing_id;
        if (Auth::o()->is('client_admin')) {
            $auth_data = Auth::o()->data();
            $testing_conds[]["`Testing`.`client_id`"] = $auth_data['client_id'];
            if (! Auth::o()->is('ARCHIVE')) {
                $testing_conds[]["`Candidate`.`archived`"] = "N";
            }
        }
	    $tmp = $Testing->find($testing_conds);

	    $testing           = @$tmp['Testing'];
	    $test              = @$tmp['Test'];
	    $candidate         = @$tmp['Candidate'];
	    $client            = @$tmp['Client'];
	    unset($tmp);

	    if (empty($testing) || empty($test) || empty($candidate) || empty($client)) {
	        throw new ArgException("Unable to restore some data from the database", 1005);
	    }

	    $Report->rel('Tests');
	    $report_conds = array();
	    $report_conds[]["`{$Report->name}`.`{$Report->primaryKey}`"] = $report_id;
	    $report_conds[]["`{$Report->name}`.`type`"] = "SIMPLE";
	    $tmp = $Report->find($report_conds);
	    $report = @$tmp['Report'];
	    if (empty($report)) {
	        throw new ArgException("Unable to search a needed report", 1007);
	    }
	    $report_ok = false;
	    foreach ($tmp['Tests'] as $_test) {
            if ($_test['id'] == $test['id']) {
                $report_ok = true;
            }
	    }
	    unset($tmp);
	    if (!$report_ok) {
	        throw new ArgException("Selected report is not suitable for this test", 1008);
	    }
	    $norm = null;
	    if ($test['type'] == 'TEST') {
    	    $norms = !empty($test['Norms']) ? $test['Norms'] : array();
    	    foreach ($norms as $_norm) {
                if ($_norm['id'] == $norm_id) {
                    $norm = $_norm;
                    break;
                }
    	    }
    	    if (empty($norm)) {
    	        throw new ArgException("Unable to search a needed norm-group", 1009);
    	    }
	    }

        $reporting_id = null;
        $Report->generateReport(array(
            'Testing'           => $testing,
            'Test'              => $test,
            'Candidate'         => $candidate,
            'Report'            => $report,
            'Norm'              => $norm,
            'Client'            => $client,
        ), $reporting_id);
		$Pass->exec('PAYMENT_REPORT', $client['id'], DomainAmount($report['price']), DomainAmount($client["balance"]), array(
            'reporting_id' => $reporting_id,
            'candidate_id' => $candidate['id'],
        ));
	    $tran->commit();
	}

	/**
	 * Создание конс. отчёта.
	 *
	 * @param int $client_id
	 * @param array $testing_ids
	 * @param int $test_id
	 * @param int $report_id
	 * @param int $norm_id
	 * @param array $params
	 */
	function generateConsReport($client_id, $testing_ids, $test_id, $report_id, $norm_id, $params=array())
	{
	    $Testing   =& $this->getModel('Testing');   /* @var $Testing   Testing */
	    $Report    =& $this->getModel('Report');    /* @var $Report    Report */
	    $Test      =& $this->getModel('Test');      /* @var $Test      Test */
	    $Client    =& $this->getModel('Client');    /* @var $Client    Client */
	    $Pass      =& $this->getModel('Pass');      /* @var $Pass      Pass */

	    $tran = new TransObject($Pass);

        if (Auth::o()->is('client_admin')) {
            $auth_data =& Auth::o()->data();
    	    if ($client_id != $auth_data["client_id"]) {
	           throw new ArgException("Prohibited operation for someone else’s client", 1015);
    	    }
        }

		$testing_conds = array();
		$testing_conds[]["`{$Testing->name}`.`{$Testing->primaryKey}`"] = $testing_ids;
		$testing_conds[]["`{$Testing->name}`.`client_id`"] = $client_id;
        if (Auth::o()->is('client_admin') && ! Auth::o()->is('ARCHIVE')) {
            $testing_conds[]["`Candidate`.`archived`"] = "N";
        }
		$testing_conds[]["`{$Testing->name}`.`test_id`"] = $test_id;
		$testing_conds[]["`{$Testing->name}`.`pass_status`"] = "PASSED";
		$Testing->rel('Test | Candidate');
		$testings = $Testing->findAll($testing_conds);

		if (empty($testings) || (count($testings) !== count($testing_ids))) {
		    throw new ArgException("Do not correct the results of tests", 1012);
		}

		$Client->rel();
		$client = $Client->find(array("`{$Client->name}`.`{$Client->primaryKey}`" => $client_id));
	    $client = @$client[$Client->name];

		$Test->rel();
	    $test = $Test->find(array("`{$Test->name}`.`{$Test->primaryKey}`" => $test_id));
	    $test = @$test[$Test->name];

	    if (empty($test) || empty($client)) {
	        throw new ArgException("Unable to restore some data from the database", 1006);
	    }

	    $Report->rel('Tests');
	    $report_conds = array();
	    $report_conds[]["`{$Report->name}`.`{$Report->primaryKey}`"] = $report_id;
	    $report_conds[]["`{$Report->name}`.`type`"] = "CONSOLIDATED";
	    $tmp = $Report->find($report_conds);
	    $report = @$tmp['Report'];
	    if (empty($report)) {
	        throw new ArgException("Unable to search a needed report", 1016);
	    }
	    $report_ok = false;
	    foreach ($tmp['Tests'] as $_test) {
            if ($_test['id'] == $test['id']) {
                $report_ok = true;
            }
	    }
	    unset($tmp);
	    if (!$report_ok) {
	        throw new ArgException("Selected report is not suitable for this test", 1017);
	    }
	    $norms = !empty($test['Norms']) ? $test['Norms'] : array();
	    $norm = array();
	    foreach ($norms as $_norm) {
            if ($_norm['id'] == $norm_id) {
                $norm = $_norm;
                break;
            }
	    }
	    if (empty($norm)) {
	        throw new ArgException("Unable to search a needed norm-group", 1018);
	    }

        $reporting_id = null;
        $Report->generateConsReport(array(
            'Test'              => $test,
            'Testings'          => $testings,
            'Report'            => $report,
            'Norm'              => $norm,
            'Client'            => $client,
        ), $reporting_id, $bad_candidates);
	    $params['bad_testings_num'] = $bad_candidates;
		$valid_testings = count($testings) - $bad_candidates;
		$price = bc::add($report['price'], bc::mul($valid_testings, $report['add_price']));
		$Pass->exec('PAYMENT_CONSOLIDATED_REPORT', $client['id'], DomainAmount($price), DomainAmount($client["balance"]), array(
            'reporting_id' => $reporting_id,
        ));
	    $tran->commit();
	}

	/**
	 * Транзакция платы за распечатку бланка.
	 *
	 * @param integer $candidate_id Ид дкандидата
	 * @param integer $testing_id Идентификатор пройденного тестирования
	 * @param integer $params Дополнительные параметры
	 */
	function printBlank($candidate_id, $testing_id, $params=array())
	{
	    $Pass    =& $this->getModel('Pass');    /* @var $Pass    Pass */
	    $Testing =& $this->getModel('Testing'); /* @var $Testing Testing */

	    $tran = new TransObject($Pass);

 		$Testing->rel('Candidate | Client | Test');
	    $testing_conds = array();
	    $testing_conds[]["`Testing`.`candidate_id`"] = $candidate_id;
	    $testing_conds[]["`Testing`.`id`"] = $testing_id;
        if (Auth::o()->is('client_admin')) {
            $auth_data = Auth::o()->data();
            $testing_conds[]["`Testing`.`client_id`"] = $auth_data['client_id'];
            if (! Auth::o()->is('ARCHIVE')) {
                $testing_conds[]["`Candidate`.`archived`"] = "N";
            }
        }
	    $tmp = $Testing->find($testing_conds);

	    $testing           = @$tmp['Testing'];
	    $test              = @$tmp['Test'];
	    $client            = @$tmp['Client'];
	    unset($tmp);

	    if (empty($testing) || empty($test) || empty($client)) {
	        throw new ArgException("Unable to restore some data from the database", 1021);
	    }

        $testing_data = array();
        $testing_data['Testing']['id'] = $testing["id"];
        $testing_data['Testing']['blank_printed'] = 'Y';
    	if (! $Testing->save($testing_data)) {
		    throw new Exception("Unable to change testing blank_printed field", 1024);
    	}
		$Pass->exec('PAYMENT_BLANK_PRINT', $client['id'], DomainAmount($test['price_blank']), DomainAmount($client["balance"]), array(
            'testing_id' => $testing_id,
            'candidate_id' => $testing['candidate_id'],
        ));
	    $tran->commit();
	}
}

?>